##  企鹅数据
##  随机森林
##https://www.kaggle.com/datasets/parulpandey/palmer-archipelago-antarctica-penguin-data
# penguins_lter.csv 归回
# penguins_size.csv 分类
# 查找最优参数GridSearchCV

import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
## 为了正确评估模型性能，将数据划分为训练集和测试集，并在训练集上训练模型，在测试集上验证模型性能。
from sklearn.model_selection import train_test_split
## 绘图函数库
import matplotlib.pyplot as plt
import seaborn as sns

data = pd.read_csv('penguins_lter.csv')  ## 加载数据 (344, 16)
## 仅选取 4个的特征 + 1个类别
data = data[['Species', 'Culmen Length (mm)', 'Culmen Depth (mm)',
             'Flipper Length (mm)', 'Body Mass (g)']]   # 取出5列数据 （344, 16)
#data = data.iloc[:, [2, 9, 10, 11, 12]] #等价

## 利用.info()查看数据的整体信息
data.info()
## 进行简单的数据查看，我们可以利用 .head() 头部.tail()尾部
print(data.head())
print(data.tail())

# 丢弃name和age这两列中有缺失值的行
data = data.dropna(axis=0, subset=['Culmen Length (mm)', 'Culmen Depth (mm)',
                                   'Flipper Length (mm)', 'Body Mass (g)'])
print(data)


# 输出三种企鹅的标签 ############################################################
labels = data['Species'].unique()
print(labels)
## 利用value_counts函数查看每个类别数量
print(pd.Series(data['Species']).value_counts())

## 对于特征进行一些统计描述
print(data.describe())

## 特征与标签组合的散点可视化
# sns.pairplot(data=data, diag_kind='hist', hue= 'Species')
## 画出箱线图
i = 1
for col in data.columns:
    if col != 'Species':
        ax = plt.subplot(2, 2, i)
        i += 1
        sns.boxplot(x='Species', y=col, saturation=0.5, palette='pastel', data=data)

        ax.set_xticklabels(range(3))
        plt.title(col)
plt.subplots_adjust(wspace =0.5, hspace =0.5)#调整子图间距
# plt.show()

## 取出数据 ####################################################
# data = data.values
# x = data[:, [2, -1]]
# y = data[:, 0]

#x = data.iloc[:, 1:]

x = data[['Culmen Length (mm)', 'Culmen Depth (mm)',
          'Flipper Length (mm)', 'Body Mass (g)']]
y = data['Species']  # 方法1，不转换数字标签，随机森林可以用转数字


# y = data['Species'].map({'Adelie Penguin (Pygoscelis adeliae)': 0,
#                          'Chinstrap penguin (Pygoscelis antarctica)': 1,
#                          'Gentoo penguin (Pygoscelis papua)': 2})   # 方法2：转换为数字标签0,1,2

# keys = data['Species'].unique()
# values = range(3)
# y = data['Species'].map(dict(zip(keys,values))) # 方法3：转换为数字标签0,1,2

from sklearn.preprocessing import LabelEncoder
# y = LabelEncoder().fit_transform(data['Species']) # 方法4：转换为数字标签0,1,2， 方法2,3,4等价
print(x.shape, y.shape)


## 测试集大小为20%， 80%/20%分 ############################################
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=2022)

# 建立模型################################################################
rf = RandomForestClassifier(n_estimators=100, criterion='gini', min_samples_split=2)
rf.fit(x_train, y_train)

# 输出模型结果##########################################################################
score = rf.score(x_train, y_train)
print(score)
score = rf.score(x_test, y_test)
print(score)

from sklearn import metrics

y_pred = rf.predict(x_test)
cm = metrics.confusion_matrix(y_test, y_pred)
print(cm)


# plt.show()
######## 输出特征重要性

# 获取特征重要性###################################################################
feature_importances = rf.feature_importances_

print(feature_importances)
plt.figure()
plt.bar([1,2,3,4], feature_importances)
# plt.show()


########################################################################
# 调参，绘制学习曲线来调参n_estimators（对随机森林影响最大）
from sklearn.model_selection import cross_val_score
# 方法1：手动交叉检验找最优参，适合单一参数

scores_cv = []
# 每隔10步建立一个随机森林，获得不同n_estimators的得分
steps = range(1, 101, 5)
# steps = range(65, 97, 2)
# for i in steps:
#     rfc = RandomForestClassifier(n_estimators=i, criterion='gini', random_state=2021)
#     score = cross_val_score(rfc, x_train, y_train, cv=5).mean()
#     scores_cv.append(score)
#
# score_max = max(scores_cv)
# index = scores_cv.index(score_max)
# print('最大得分：{}'.format(score_max),
#       '子树数量为：{}'.format(steps[index]))

from sklearn.model_selection import GridSearchCV
# 方法2：
# GridSearchCV
rang = range(1, 101, 5)

parameters = {'n_estimators' : rang, 'min_samples_leaf': (1,2)}
np.random.seed(2025)
gs = GridSearchCV(RandomForestClassifier(), param_grid=parameters, cv=5, verbose=1)
gs.fit(x_train, y_train)

print('最佳参数：', gs.best_params_)
# n_estimators = gs.best_params_['n_estimators']
# min_samples_leaf = = gs.best_params_['min_samples_leaf']

# 绘制学习曲线##########################################################################
plt.figure()
plt.plot(rang, gs.cv_results_['mean_test_score'])
plt.show()
input()

###用最优子树数量去建立随机森林，并用测试集验证
rf = RandomForestClassifier(**gs.best_params_, criterion='gini', random_state=2025)
rf.fit(x_train, y_train)
print('测试集准确率：', rf.score(x_test, y_test))

plt.show()
